### Dynamic memory allocation with C
Learning C is like learning to drive a manual car: it gives you a deeper understanding of the underlying mechanisms that higher-level languages abstract away.
In C, you can see how fundamental data structures like strings, hashmaps, and arrays are implemented and represented in memory.
In contrast, high-level languages hide these details from the programmer, managing them automatically.
Studying C also helps you appreciate the challenges faced by programmers when designing programming languages, particularly in handling dynamic data structures.
Unlike numbers, structures like strings and lists have unpredictable sizes, requiring careful management of dynamic memory allocation.

C introduces data types that have specific sizes in memory (`int` , `float`, `long` and `char` ) . If I want to represent the number 47, I typically use an `int`, which takes 4 bytes in my computer's memory—equivalent to 32 bits. But what if you want the user to input their name? How many `char` would you need? The answer is: we don’t know, because names vary in length.
##### Strings
Strings are inherently dynamic, so we needed a strategy to represent them efficiently in memory. To solve this, it was decided to terminate strings with a NUL `\0` character. This allowed us to determine where a string ends. Alright, but how do we determine where a string starts? For that, we use pointers, which store memory addresses of variables. So to represent a string, we typically store a memory address (indicating where the string starts) and end it with a NUL character `\0`.
##### Arrays
Our second challenge with dynamic data relates to arrays. In C, arrays are not dynamic. If we want to add a new element to an array, we need to allocate new memory, copy all the existing elements into it, and then add the new value. This process is inefficient, especially for large arrays. Once again, pointers come to the rescue. To solve the issue of array dynamism, we use pointers to create linked lists. A linked list is a collection of nodes, where each node contains a value and a pointer to the next node. This approach allows us to dynamically add elements to the list without needing to find a contiguous block of memory, unlike arrays which must be allocated as a single, continuous chunk.
